# -*-coding:utf-8-*-

from tests.apis.http import *
from tests.apis.base import BaseAPI

import time
import urllib
from utils.sign import sign_arguments
from tests import configs
from bson.objectid import ObjectId
from tornado.gen import coroutine, moment, sleep, Return
from tornado.ioloop import IOLoop
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
import functools


class Users(BaseAPI):
    def __init__(self, *args, **kwargs):
        super(Users, self).__init__(*args, **kwargs)
        if 'uid' in kwargs:
            self.uid = kwargs['uid']

    def build_common_arguments(self):
        arguments = {}
        arguments['appkey'] = configs.TEST_APP_KEY  # '93F9C5720748E3F37F591D295EF61A8A'
        arguments['timestamp'] = int(time.time())
        return arguments

    def _get_register_code(self, username):
        arguments = self.build_common_arguments()
        arguments['recnum'] = username
        sign_arguments(arguments)
        query = urllib.urlencode(arguments)
        response = http_get('api/users/register/code', query)
        self.assertEqual(response.code, 200, response.reason)
        response = json.loads(response.body)
        if not response['result']:
            self.assertTrue(10400 == response['error_code'], response['error_message'])
            return False
        return response['data']['code']

    def register(self, username=configs.USERNAME, password=configs.PASSWORD, specifics=None):
        code = self._get_register_code(username)
        if not code:
            return
        arguments = self.build_common_arguments()
        arguments['username'] = username
        arguments['password'] = password
        arguments['code'] = code
        if specifics is not None:
            arguments['specifics'] = specifics
        sign_arguments(arguments)
        body = arguments
        response = http_post('api/users/register', body)
        self.assertEqual(response.code, 200, response.reason)
        response = json.loads(response.body)
        self.assertTrue(response['result'] or 10400 == response['error_code'],
                        response['error_message'])

    def _get_bind_code(self, username):
        arguments = self.build_common_arguments()
        arguments['recnum'] = username
        sign_arguments(arguments)
        query = urllib.urlencode(arguments)
        response = http_get('api/users/bind/code', query)
        self.assertEqual(response.code, 200, response.reason)
        response = json.loads(response.body)
        return response['data']['code']

    def bind(self, username=configs.USERNAME, password=configs.PASSWORD, specifics=None, user_bind_info=None):
        code = self._get_bind_code(username)
        arguments = self.build_common_arguments()
        arguments['username'] = username
        arguments['password'] = password
        arguments['code'] = code
        if specifics is not None:
            arguments['specifics'] = specifics
        sign_arguments(arguments)
        body = arguments
        headers = dict(cookie='user_bind_info=%s;' % user_bind_info)
        response = http_post('api/users/bind', query='appkey=%s' % configs.TEST_APP_KEY, body=body, headers=headers)
        self.assertEqual(response.code, 200, response.reason)
        response = json.loads(response.body)
        return response
    
    def bind_consumers(self, appkey, user_bind_info=None):
        headers = dict(cookie='user_bind_info=%s;' % user_bind_info)
        response = http_post('api/users/bind/consumers', query='appkey=%s' % appkey, body={}, headers=headers)
        self.assertEqual(response.code, 200, response.reason)
        response = json.loads(response.body)
        return response

    @coroutine
    def _simulate_wx_auth(self, wait_id, wx_mp_openid):
        # IMPORTANT!? but it seem to be not necessary
        yield moment
        yield moment
        yield moment
        yield sleep(0.1)

        arguments = dict()
        arguments['wait_id'] = wait_id
        import handlers.weixin.mp
        arguments['state'] = handlers.weixin.mp.STATE
        arguments['code'] = 'CODE'
        arguments['appkey'] = configs.TEST_APP_KEY
        arguments['wx_mp_openid'] = wx_mp_openid
        query = urllib.urlencode(arguments)
        url = url_compose('mp/on_auth', query=query)
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch(url, raise_error=False)
        self.assertEqual(response.code, 200, response.reason)

    @coroutine
    def _auth_by_wx_coroutine(self, *args, **kwargs):
        # TODO: extract with_id from the Set-Cookie header field that return from API 'GET /api/users/auth/qrcode'
        wait_id = str(ObjectId())
        wx_mp_openid = kwargs['wx_mp_openid']
        # NOTE: do not yield here! and yield 'f' is not necessary
        f = self._simulate_wx_auth(wait_id, wx_mp_openid)
        url = url_compose('api/users/auth/result')
        headers = dict(cookie='wait_id=%s;' % wait_id)
        http_request = HTTPRequest(url, headers=headers)
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch(http_request, raise_error=False)
        self.assertEqual(response.code, 200, response.reason)
        response = json.loads(response.body)
        result = response['data']

        if isinstance(result, (str, unicode)):
            bind_consumers = kwargs.get('bind_consumers', False)
            if bind_consumers:
                appkey = kwargs.get('appkey', configs.TEST_APP_KEY)
                response = self.bind_consumers(appkey, user_bind_info=result)
            else:
                specifics = kwargs['specifics']
                username = kwargs['username'] if 'username' in kwargs else configs.USERNAME
                password = kwargs['password'] if 'password' in kwargs else configs.PASSWORD
                response = self.bind(specifics=specifics, user_bind_info=result, username=username, password=password)
                # username = configs.USERNAME
                # code = self._get_bind_code(username)
                # arguments = dict(username=username, code=code, specifics=specifics)
                # url = url_compose('api/users/bind', query='appkey=%s' % configs.TEST_APP_KEY)
                # headers = dict(cookie='user_bind_info=%s;' % result)
                # body = json.dumps(arguments)
                # http_request = HTTPRequest(url, headers=headers, method='POST', body=body)
                # http_client = AsyncHTTPClient()
                # response = yield http_client.fetch(http_request, raise_error=False)
                # self.assertEqual(response.code, 200, response.reason)
                # response = json.loads(response.body)
                # result = response['data']
            if not response['result']:
                raise Return(response)
            result = response['data']
        else:
            self.assertTrue(isinstance(result, dict) and 'access_token' in result)
        self.uid = result['user_id']
        self.access_token = result['access_token']
        self.query = 'access_token=%s' % self.access_token

    def auth_by_wx(self, *args, **kwargs):
        result = IOLoop.current().run_sync(functools.partial(self._auth_by_wx_coroutine, *args, **kwargs), 10)
        return result

    def unregister(self, username=configs.USERNAME):
        arguments = self.build_common_arguments()
        sign_arguments(arguments)
        query = urllib.urlencode(arguments)
        response = http_delete('test/api/users/%s' % username, query=query)
        self.assertEqual(response.code, 200, response.reason)

    def login_guest(self):
        arguments = self.build_common_arguments()
        sign_arguments(arguments)
        query = urllib.urlencode(arguments)
        response = http_get('api/users/login/guest', query)
        self.assertEqual(response.code, 200, response.reason)
        response = json.loads(response.body)
        self.assertTrue(response['result'], response['error_message'])
        self.access_token = response['data']['access_token']
        self.uid = response['data']['user_id']
        self.query = 'access_token=%s' % self.access_token

    def login(self, username=configs.USERNAME, password=configs.PASSWORD):
        arguments = self.build_common_arguments()
        arguments['username'] = username
        arguments['password'] = password
        sign_arguments(arguments)
        query = urllib.urlencode(arguments)
        response = http_get('api/users/login', query)
        self.assertEqual(response.code, 200, response.reason)
        response = json.loads(response.body)
        self.assertTrue(response['result'], response['error_message'])
        self.access_token = response['data']['access_token']
        self.uid = response['data']['user_id']
        self.query = 'access_token=%s' % self.access_token

    def get_member_list(self, *args, **kwargs):
        extra_query = urllib.urlencode(kwargs)
        query = self.query
        if 0 != len(extra_query):
            query += '&' + extra_query
        response = http_get('platform/api/users/%s/members' % self.uid, query)
        return self.assert_response_success_data(response)

    def get_member_count(self, *args, **kwargs):
        extra_query = urllib.urlencode(kwargs)
        query = self.query
        if 0 != len(extra_query):
            query += '&' + extra_query
        response = http_get('platform/api/users/%s/members/count' % self.uid, query)
        return self.assert_response_success_data(response)

    def get_role_list(self, *args, **kwargs):
        response = http_get('platform/api/users/%s/roles' % self.uid, self.query)
        return self.assert_response_success_data(response)

    def create_member(self, *args, **kwargs):
        response = http_post('platform/api/users/%s/members' % self.uid, kwargs, self.query)
        return self.assert_response_success_data(response)

    def get_member(self, *args, **kwargs):
        mid = kwargs['mid']
        response = http_get('platform/api/users/%s/members/%s' % (self.uid, mid), self.query)
        return self.assert_response_success_data(response)

    def update_member(self, *args, **kwargs):
        mid = kwargs['mid']
        response = http_put('platform/api/users/%s/members/%s' % (self.uid, mid), kwargs, self.query)
        return self.assert_response_success_data(response)

    def delete_member(self, *args, **kwargs):
        mid = kwargs['mid']
        response = http_delete('platform/api/users/%s/members/%s' % (self.uid, mid), query=self.query)
        return self.assert_response_success_data(response)

    def get_user(self, *args, **kwargs):
        response = http_get('api/users/%s' % self.uid, self.query)
        return self.assert_response_success_data(response)

    def get_user_address_list(self, *args, **kwargs):
        response = http_get('api/users/%s/addresses' % self.uid, self.query)
        return self.assert_response_success_data(response)

    def get_user_address(self, *args, **kwargs):
        address_id = kwargs.pop('address_id')
        response = http_get('api/users/%s/addresses/%s' % (self.uid, address_id), self.query)
        return self.assert_response_success_data(response)

    def add_user_address(self, *args, **kwargs):
        response = http_post('api/users/%s/addresses' % self.uid, kwargs, self.query)
        return self.assert_response_success_data(response)

    def update_user_address(self, *args, **kwargs):
        address_id = kwargs.pop('address_id')
        response = http_put('api/users/%s/addresses/%s' % (self.uid, address_id), kwargs, self.query)
        return self.assert_response_success_data(response)

    def delete_user_address(self, *args, **kwargs):
        address_id = kwargs.pop('address_id')
        response = http_delete('api/users/%s/addresses/%s' % (self.uid, address_id), query=self.query)
        return self.assert_response_success_data(response)

    def _get_user_password_reset_code(self, username):
        arguments = self.build_common_arguments()
        arguments['recnum'] = username
        sign_arguments(arguments)
        query = urllib.urlencode(arguments)
        response = http_get('api/users/password/code', query)
        self.assertEqual(response.code, 200, response.reason)
        response = json.loads(response.body)
        if not response['result']:
            self.assertTrue(10400 == response['error_code'], response['error_message'])
            return False
        return response['data']['code']

    def reset_user_password(self, **kwargs):
        username = kwargs['username']
        password = kwargs['password']
        code = self._get_user_password_reset_code(username)
        if not code:
            return
        arguments = self.build_common_arguments()
        arguments['username'] = username
        arguments['password'] = password
        arguments['code'] = code
        sign_arguments(arguments)
        body = arguments
        response = http_put('api/users/password', body=body, query=self.query)
        return self.assert_response_success_data(response)
